#include"preCmpH.h"
#include"UIRenderer.h"
#include"UIFont.h"
#include"UIFontManager.h"

float* genMatrix(float width, float height)
{
	float* temp = new float[16];
    temp[0] = 2 / width;
    temp[1] = 0;
    temp[2] = 0;
    temp[3] = -1;

    temp[4] = 0;
    temp[5] = -2 / height;
    temp[6] = 0;
    temp[7] = 1;

    temp[8] = 0;
    temp[9] = 0;
    temp[10] = -1;
    temp[11] = 0;

    temp[12] = 0;
    temp[13] = 0;
    temp[14] = 0;
    temp[15] = 1;
	return temp;
}
float* genMatrix(int width, int height)
{
	float* temp = new float[16];
	temp[0] = 2 / (float)width;
	temp[1] = 0;
	temp[2] = 0;
	temp[3] = -1;

	temp[4] = 0;
	temp[5] = -2 / (float)height;
	temp[6] = 0;
	temp[7] = 1;

	temp[8] = 0;
	temp[9] = 0;
	temp[10] = -1;
	temp[11] = 0;

	temp[12] = 0;
	temp[13] = 0;
	temp[14] = 0;
	temp[15] = 1;
	return temp;
}
int __error;
bool hasErrors()
{
	if(__error = glGetError()){
		return true;
	}
	return false;
}
void PostErrors()
{
	std::cout << gluErrorString(__error);
	while ((__error = glGetError()) != GL_NO_ERROR) {
		std::cout << gluErrorString(__error);
	}
}

BEGIN_NAMESPACE(ui_manager)
BEGIN_NAMESPACE(ui_graphics)
//=====================================
// Constructors, Destructor, Operators
//=====================================

UIRenderer::UIRenderer(){}
UIRenderer::~UIRenderer(){}
//=====================================
// Functionality
//=====================================

UIImage& UIRenderer::GenerateImage(std::string name,int width,int height, bool transp,int hex_color)
{
	UIImage* img = getImage(name);
	if(img != null && !img->IsEmpty()){
		return *img;
	}
	int sizes = width * height * 4;
	unsigned char* pixels = new unsigned char[sizes];
	if(transp){
		memset(pixels,0,sizes);
	} else {
		for(int i = 0; i < sizes; i++){
			memset(pixels + i, hex_color >> (3 - (i % 4)) * 8, 1);
		}
	}

	uint id = 0;
	glGenTextures(1,&id);
	glBindTexture(GL_TEXTURE_2D, id);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_REPEAT);
	glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, width, height, 0,GL_RGBA, GL_UNSIGNED_BYTE,pixels);

	img = new UIImage();
	img->SetWidth(width);
	img->SetHeight(height);
	img->SetId(id);
	img->SetName(name);
	img->SetFormat(UIImage::FORMAT_RGBA);
	img->SetType(UIImage::TYPE_U_BYTE);
	images_.push_front(img);
	// cleanup
	glBindTexture(GL_TEXTURE_2D, 0);
	delete pixels;

	return *img;
}
UIImage& UIRenderer::LoadImageFromFile(std::string name,std::string path)
{
	UIImage* img = getImage(name);
	if(img != null){
		return *img;
	}
	uint image1;
	ilGenImages(1,&image1);
	ilBindImage(image1);
	ilLoadImage(path.c_str());
	int width = ilGetInteger(IL_IMAGE_WIDTH);
	int height = ilGetInteger(IL_IMAGE_HEIGHT);
	int sizes = width * height * 4;
	unsigned char* pixels = new unsigned char[sizes];
	ilCopyPixels(0, 0, 0, width, height, 1, IL_RGBA, IL_UNSIGNED_BYTE, pixels);

	uint id = 0;
	glGenTextures(1,&id);
	glBindTexture(GL_TEXTURE_2D, id);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_REPEAT);
	glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, width, height, 0,GL_RGBA, GL_UNSIGNED_BYTE,pixels);

	img = new UIImage();
	img->SetWidth(width);
	img->SetHeight(height);
	img->SetId(id);
	img->SetName(name);
	img->SetFormat(UIImage::FORMAT_RGBA);
	img->SetType(UIImage::TYPE_U_BYTE);
	images_.push_front(img);

	// cleanup
	glBindTexture(GL_TEXTURE_2D, 0);
	ilDeleteImage(image1);
	delete pixels;

	return *img;
}
void UIRenderer::ClearImage(UIImage& img)
{
	uint id = img.GetId();
	if(id != 0){
		glDeleteTextures(1, &id);
	}
	img.SetId(0);
}
void UIRenderer::DrawImage(int x, int y, int w, int h, const UIImage& img)
{
	if(matrix_ == null){
		UIException e("Member matrix_ is not initialized",
						"UIRenderer.cpp",
						"UIRenderer",
						"DrawImage");
		throw e;
	}
	// set shader program
	glUseProgram(shader_program_);
	//
	glUniformMatrix4fv(proj_matrix_loc_,1,true,matrix_);
	// set the values for pos and scale in shade
	glUniform2f(pos_loc_, (GLfloat)x, (GLfloat)y);
	glUniform2f(scale_loc_, (GLfloat)w, (GLfloat)h);
	// bind activate and intialize texture
	glBindTexture(GL_TEXTURE_2D, img.GetId());
	glActiveTexture(GL_TEXTURE0);
	glUniform1i(texture_loc_, 0);
	// draw
	glDrawArrays(GL_TRIANGLES, RECTANGLE_START, RECTANGLE_STOP);
	// clean up
	glUniform2f(pos_loc_, 0, 0);
	glUniform2f(scale_loc_, 1, 1);
	glBindTexture(GL_TEXTURE_2D, 0);
	glUseProgram(0);

}
void UIRenderer::DrawTextToImage(std::string text,ui_fonts::UIFont& font,UIImage& save)
{
	if(text.empty()){
		return;
	}

	ui_fonts::UIFontManager::getInstance().LoadText(font, text);

	UISize size = ui_fonts::UIFontManager::getInstance().GetTextSize();
	save.Clear();
	save = GenerateImage(save.GetName(), size.getWidth(), size.getHeight(), true, 0);

	glBindTexture(GL_TEXTURE_2D, save.GetId());
	ui_fonts::UIFontChar char_img;
	for(uint i = 0; i <text.length(); i++)
	{
		char_img = ui_fonts::UIFontManager::getInstance().GetChar(i);
		glTexSubImage2D(GL_TEXTURE_2D, 0,
			char_img.GetPosX(),
			char_img.GetPosY(),
			char_img.GetWidth(),
			char_img.GetHeight(),
			GL_ALPHA,
			GL_UNSIGNED_BYTE,
			char_img.GetBuffer() );
	}
	ui_fonts::UIFontManager::getInstance().Clear();
	glBindTexture(GL_TEXTURE_2D, 0);
}
void UIRenderer::DisableClipping()
{
	glClear(GL_STENCIL_BUFFER_BIT);
	glStencilFunc(GL_ALWAYS, 1, 1);
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
void  UIRenderer::DrawLine(int x1, int y1, int x2, int y2, int _0xColor)
{
	int* temp = new int[4];
	temp[0] = x1; temp[1] = y1;
	temp[2] = x2; temp[3] = y2;
	glBindBuffer(GL_ARRAY_BUFFER,vertex_buffer_);
	int offset = LINE_START * 2 * sizeof(int);
	glBufferSubData(GL_ARRAY_BUFFER, offset, 4 * sizeof(int),temp);
	// set shader program
	glUseProgram(shader_program_);
	//
	glUniformMatrix4fv(proj_matrix_loc_,1,true,matrix_);
	// set the values for pos and scale in shade
	glUniform4f(color_loc_, 0, 0, 0, 1);
	// draw
	glDrawArrays(GL_LINES, LINE_START, LINE_STOP);
	// clean up
	glUniform4f(color_loc_, 0, 0, 0, 0);
	glUseProgram(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	delete temp;
}
//=====================================
// Setters
//=====================================

void UIRenderer::setClipping(int x, int y, int w, int h)
{
	// set shader program
	glUseProgram(shader_program_);
	//
	glUniformMatrix4fv(proj_matrix_loc_,1,true,matrix_);
	// set the values for pos and scale in shade
	glUniform2f(pos_loc_, (GLfloat)x, (GLfloat)y);
	glUniform2f(scale_loc_, (GLfloat)w, (GLfloat)h);

	glStencilFunc(GL_ALWAYS, 1, 1);
	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	glDrawArrays(GL_TRIANGLES, RECTANGLE_START, RECTANGLE_STOP - RECTANGLE_START);

	glUseProgram(0);

	glStencilFunc(GL_EQUAL, 1, 1);
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
void UIRenderer::setScreenSize(UISize size)
{
	size_ = size;
	glViewport(0,0,size_.getWidth(),size_.getHeight());
	matrix_ = genMatrix(size_.getWidth(), size_.getHeight());
}
//=====================================
// Getters
//=====================================

UIRenderer& UIRenderer::getInstance()
{
	static UIRenderer* instance = null;
	if(instance == null)
	{
		instance = new UIRenderer();
	}
	return *instance;
}

void UIRenderer::StartUp( UIManager& manager )
{
	ilInit();
	ILenum error = ilGetError();
	if(error){
		throw std::exception("UIGraphics failed to initialize");
	}
	shader_program_ = ShaderProgram( "ui_vshader.txt", "ui_fshader.txt" );

	int points[][2] = {{ 0, 1},{ 1, 1},{ 0, 0},
	{ 0, 0},{ 1, 1},{ 1, 0},
	{ 0, 0},{ 0, 0}};

	int texels[][2] = {{ 0, 1},{ 1, 1},{ 0, 0},
	{ 0, 0},{ 1, 1},{ 1, 0}};

	glGenBuffers(1,&vertex_buffer_);
	glBindBuffer( GL_ARRAY_BUFFER, vertex_buffer_ );
	glBufferData( GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW );

	glGenBuffers(1,&texel_buffer_);
	glBindBuffer( GL_ARRAY_BUFFER, texel_buffer_ );
	glBufferData( GL_ARRAY_BUFFER, sizeof(texels), texels, GL_STATIC_DRAW );

	glBindBuffer( GL_ARRAY_BUFFER, vertex_buffer_ );
	GLuint loc = glGetAttribLocation( shader_program_, "vPosition" );
	glVertexAttribPointer( loc, 2, GL_INT, GL_FALSE, 0, 0 );
	glEnableVertexAttribArray( loc );

	glBindBuffer( GL_ARRAY_BUFFER, texel_buffer_ );
	GLuint loctex = glGetAttribLocation( shader_program_, "vTexel" );
	glVertexAttribPointer( loctex, 2, GL_INT, GL_FALSE, 0, 0 );
	glEnableVertexAttribArray( loctex );

	color_loc_ = glGetUniformLocation(shader_program_, "color");
	pos_loc_ = glGetUniformLocation(shader_program_, "pos");
	scale_loc_ = glGetUniformLocation(shader_program_, "scale");
	texture_loc_ = glGetUniformLocation(shader_program_, "texture");
	proj_matrix_loc_ = glGetUniformLocation(shader_program_, "projection");
	matrix_ = null;

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glClearColor(1,0,1,1);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	//glEnable(GL_DEPTH_TEST);
	glEnable(GL_STENCIL_TEST);
	glClearStencil(0);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}

void UIRenderer::ShutDown()
{
	delete[] matrix_;
}

UIImage* UIRenderer::getImage( std::string name )
{
	std::list<UIImage*>::iterator it;
	for(it = images_.begin(); it != images_.end(); it++){
		if( (*it)->GetName() == name ){
			return (*it);
		}
	}
	return null;
}

END_NAMESPACE // UI manager
END_NAMESPACE // UI graphics
